﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using MartinsEssentials.MVVM;

namespace MALRECrewrite.Models
{
    public class SeasonAnime : ViewModelBase
    {
        public SeasonAnime()
        {
        }

        public SeasonAnime(int aid, string title, bool isinlist, string uri)
        {
            Aid = aid;
            Title = title;
            IsInAnimelist = isinlist;
            Uri = uri;
        }

        private bool _IsRootAnime = false;

        public bool IsRootAnime
        {
            get
            {
                return _IsRootAnime;
            }
            set
            {
                _IsRootAnime = value;
            }
        }

        private bool _IsOnBottom = false;

        public bool IsOnBottom
        {
            get
            {
                return _IsOnBottom;
            }
            set
            {
                if (value != _IsOnBottom)
                {
                    _IsOnBottom = value;
                    OnPropertyChanged("IsOnBottom");
                }
            }
        }

        private bool _IsOnTop = false;

        public bool IsOnTop
        {
            get
            {
                return _IsOnTop;
            }
            set
            {
                if (value != _IsOnTop)
                {
                    _IsOnTop = value;
                    OnPropertyChanged("IsOnTop");
                }
            }
        }

        public List<int> ReferenceAids { get; set; }
        public List<int> ParentAids { get; set; }

        [XmlIgnore]
        private ObservableCollection<SeasonAnime> _References = new ObservableCollection<SeasonAnime>();

        [XmlIgnore]
        public ObservableCollection<SeasonAnime> References
        {
            get
            {
                return _References;
            }
            set
            {
                if (value != _References)
                {
                    _References = value;
                    OnPropertyChanged("References");
                }
            }
        }

        [XmlIgnore]
        public List<SeasonAnime> Parents = new List<SeasonAnime>();

        private List<MALRECrewrite.Models.Reference.ReferenceType> _ReferenceTypes = new List<MALRECrewrite.Models.Reference.ReferenceType>();

        public List<MALRECrewrite.Models.Reference.ReferenceType> ReferenceTypes
        {
            get
            {
                return _ReferenceTypes;
            }
            set
            {
                if (value != _ReferenceTypes)
                {
                    _ReferenceTypes = value;
                    OnPropertyChanged("ReferenceTypes");
                }
            }
        }

        public int Aid { get; set; }

        public string Title { get; set; }

        public bool IsInAnimelist { get; set; }

        public string Uri { get; set; }

        public void AddToReferences(SeasonAnime sr, MALRECrewrite.Models.Reference.ReferenceType t)
        {
            References.Add(sr);
            ReferenceTypes.Add(t);
            //References.Add(new Reference() { Ref = sr, Type = t });
        }

        public void RemoveParents()
        {
            List<SeasonAnime> toRemove = new List<SeasonAnime>();
            for (int i = 0; i < ReferenceTypes.Count; i++)
            {
                if (ReferenceTypes[i] == Reference.ReferenceType.parent_story || ReferenceTypes[i] == Reference.ReferenceType.prequel)
                {
                    toRemove.Add(References[i]);
                }
            }

            if (toRemove.Count == 0)
                _IsRootAnime = true;
            else
            {
                foreach (SeasonAnime s in toRemove)
                {
                    Parents.Add(s);
                    References.Remove(s);
                }
            }
        }

        public void RemoveRecursion(List<SeasonAnime> roots)
        {
            foreach (SeasonAnime s in roots.Where(x => x.Aid != Aid && x.References.Count > 0))
            {
                List<int> alreadyChecked = new List<int>();
                bool? result = s.ContainsSeasonAnime(this, alreadyChecked);
                if (result == true)
                {
                    //_IsRootAnime = false;
                    break;
                }
                else if (result == null)
                {
                    Debug.WriteLine("Zyklus: "+s.Title + " AlreadyChecked: "+alreadyChecked.Count);
                    if (alreadyChecked.Count > 2)
                    {
                        SeasonAnime a = App.SequelsModel.SequelRefs.Where(x => x.Aid == alreadyChecked[alreadyChecked.Count - 2]).First();
                        a.References.Remove(a.References.Where(x => x.Aid == alreadyChecked.Last()).FirstOrDefault());
                        a.IsRootAnime = false;
                    }
                    //if (ReferenceToOtherParent(this))
                    //    IsRootAnime = false;
                    //else if (ReferenceToOtherParent(s))
                    //{
                    //    s.IsRootAnime = false;
                    //    RemoveReference(s, alreadyChecked.Last());
                    //}
                }
            }
        }

        public bool ReferenceToOtherParent(SeasonAnime a)
        {
            foreach (SeasonAnime s in a.References)
            {
                if (s.Parents.Count(x => x.Aid == a.Aid) == 0)
                {
                    return true;
                }
            }

            return false;
        }

        public bool RemoveReference(SeasonAnime s, int referenceTo)
        {
            if (s.Aid == Aid)
            {
                References.Where(x => x.Aid == referenceTo).ToList().ForEach(h => References.Remove(h));
                return true;
            }

            foreach (SeasonAnime a in References.Where(x => x.References.Count > 0))
            {
                if (a.RemoveReference(s, referenceTo))
                    return true;
            }

            return false;
        }

        public List<SeasonAnime> GetChildren(List<SeasonAnime> childs)
        {
            References.ToList().ForEach(x => childs.Add(x));

            foreach (SeasonAnime sa in References)
            {
                childs = sa.GetChildren(childs);
            }

            return childs;
        }

        //public int TreeLength(SeasonAnime s)
        //{
        //    return FindMaxDepth(s, new List<int>());
        //}

        //public int FindMaxDepth(SeasonAnime s, List<int> alreadyChecked, int currentDepth = 0)
        //{
        //    if (alreadyChecked.Contains(Aid))
        //        return 0;
        //    else
        //        alreadyChecked.Add(Aid);

        //    currentDepth++;
        //    int maxDepth = 0;
        //    foreach (SeasonAnime a in References.Where(x => x.References.Count > 0))
        //    {
        //        int result = a.FindMaxDepth(s, alreadyChecked, currentDepth);

        //        if (result > maxDepth)
        //            maxDepth = result;
        //    }

        //    return currentDepth + maxDepth;
        //}

        public bool? ContainsSeasonAnime(SeasonAnime s, List<int> alreadyChecked)
        {
            if (References.Contains(s))
                return true;

            if (alreadyChecked.Contains(Aid))
            {
                alreadyChecked.Add(Aid);
                return null;
            }
            else
                alreadyChecked.Add(Aid);

            foreach (SeasonAnime a in References.Where(x => x.References.Count > 0))
            {
                bool? result = a.ContainsSeasonAnime(s, alreadyChecked);
                if (result != false)
                    return result;
            }

            return false;
        }

        public void CreateReferencesFromAids()
        {
            App.SequelsModel.SequelRefs.Where(x => ReferenceAids.Contains(x.Aid)).ToList().ForEach(x => References.Add(x));
            App.SequelsModel.SequelRefs.Where(x => ParentAids.Contains(x.Aid)).ToList().ForEach(x => Parents.Add(x));
        }
    }

    public class Reference
    {
        public enum ReferenceType
        {
            prequel = 1,
            parent_story = 2,
            sequel = 4,
            side_story = 8,
            spin_off = 16,
            root = 32
        }

        public ReferenceType Type { get; set; }

        public SeasonAnime Ref { get; set; }
    }
}